use std::{fs::File, path::PathBuf};

use db::DbConfig;
use gateway::GatewayConfig;
use log::LogConfig;
use web::WebConfig;

use serde::{Deserialize, Serialize};
mod db;
mod gateway;
mod log;
mod web;

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(default)]
pub struct Config {
    // 自动更新
    pub auto_update: bool,
    // 开机启动
    pub auto_start: bool,

    #[serde(rename = "app")]
    pub common: WebConfig,
    #[serde(rename = "gateway")]
    pub vista_gateway: GatewayConfig,
    #[serde(rename = "lab")]
    pub lab_gateway: GatewayConfig,
    pub log: LogConfig,
    pub db: DbConfig,
}

impl Config {
    pub fn flush(&self, path: PathBuf) -> Result<(), String> {
        let f = File::create(path).expect("failed open config file");
        serde_yaml::to_writer(f, &self).expect("failed write  config file");
        Ok(())
    }
}

impl From<PathBuf> for Config {
    fn from(value: PathBuf) -> Self {
        match File::open(value) {
            Ok(f) => match serde_yaml::from_reader::<_, Self>(f) {
                Ok(config) => config,
                Err(e) => {
                    println!("Error parsing config file:{}", e);
                    Self::default()
                }
            },
            Err(e) => {
                println!("Error opening config file:{}", e);
                Self::default()
            }
        }
    }
}

impl From<&str> for Config {
    fn from(value: &str) -> Self {
        let config = serde_yaml::from_str::<Config>(value);
        match config {
            Ok(config) => config,
            Err(e) => {
                println!("Error parsing config file:{}", e);
                Self::default()
            }
        }
    }
}

impl Default for Config {
    fn default() -> Self {
        Self {
            auto_update: true,
            auto_start: false,
            common: WebConfig::new(),
            vista_gateway: GatewayConfig::new(50001),
            lab_gateway: GatewayConfig::new(50002),
            log: LogConfig::new(),
            db: DbConfig::new(),
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn test_from() {
        let raw = r#"
autoUpdate: true
autoStart: false
app:
    port: 8888 
    https: false
    certFile: cert/ews.pem
    keyFile: cert/ews.key

gateway:
    listen: 8889
    tls: false
    certFile: cert/gateway.pem
    keyFile: cert/gateway.key

lab:
    listen: 9000
    tls: false
    certFile: cert/labdata.pem
    keyFile: cert/labdata.key

log:
    maxSize: 10
    maxBackups: 3
    maxAge: 28
    compress: true
    operMaxSize: 30

db:
    host: 192.168.2.63
    port: 3306
    username: root
    password: cm9vdA==
    database: ews
"#;

        let config = Config::from(raw);

        assert_eq!(config.auto_start, false);
        assert_eq!(config.auto_update, true);
        assert_eq!(config.common.port, 8888);
        assert_eq!(config.common.https, false);
        assert_eq!(config.common.cert_file, "cert/ews.pem");
        assert_eq!(config.common.key_file, "cert/ews.key");
        assert_eq!(config.vista_gateway.listen, 8889);
        assert_eq!(config.vista_gateway.tls, false);
        assert_eq!(config.vista_gateway.cert_file, "cert/gateway.pem");
        assert_eq!(config.vista_gateway.key_file, "cert/gateway.key");
        assert_eq!(config.lab_gateway.listen, 9000);
        assert_eq!(config.lab_gateway.tls, false);
        assert_eq!(config.lab_gateway.cert_file, "cert/labdata.pem");
        assert_eq!(config.lab_gateway.key_file, "cert/labdata.key");
        assert_eq!(config.log.max_size, 10);
        assert_eq!(config.log.max_backups, 3);
        assert_eq!(config.log.max_age, 28);
        assert_eq!(config.log.compress, true);
        assert_eq!(config.log.oper_max_size, 30);
        assert_eq!(config.db.host, "192.168.2.63");
        assert_eq!(config.db.port, 3306);
        assert_eq!(config.db.username, "root");
        assert_eq!(config.db.password, "cm9vdA==");
        assert_eq!(config.db.database, "ews");
    }
}
